package probe.sound.code;

import java.io.InputStream;

import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.DataLine;
import javax.sound.sampled.LineEvent;
import javax.sound.sampled.LineListener;
import javax.sound.sampled.SourceDataLine;

import probe.sound.resources.Resources;

public class PlayWav {
  public static void main(String[] args) throws Exception {
    InputStream wavIn = Resources.class.getResourceAsStream( "file48000.wav" );
    byte header[] = new byte[44];
    int bytesForHeader = wavIn.read( header );
    
    if (bytesForHeader != header.length) {
      throw new RuntimeException( "No header: size = " + bytesForHeader );
    }
    int sampleRate = Util.readInt4( header, 24, true );
    int numChannels = Util.readWord( header, 22, true );
    int bitsPerSample = Util.readWord( header, 34, true );
    int blockAlign = Util.readWord( header, 32, true );
    
    if (sampleRate != 48000) {
      throw new RuntimeException( "Wronge sampleRate = " + sampleRate + ", must be 48000" );
    }
    if (numChannels != 2) {
      throw new RuntimeException( "Wronge numChannels = " + numChannels + ", must be 2" );
    }
    if (bitsPerSample != 16) {
      throw new RuntimeException( "Wronge bitsPerSample = " + bitsPerSample + ", must be 16" );
    }
    if (blockAlign != 4) {
      throw new RuntimeException( "Wronge blockAlign = " + blockAlign + ", must be 4" );
    }
    
    int dataSize = Util.readInt4( header, 40, true );
    
    byte[] data = new byte[dataSize];
    
    int readDataSize = wavIn.read( data );
    
    if (readDataSize != data.length) {
      throw new RuntimeException( "Not enaught data: readDataSize = " + readDataSize
          + ", but must be " + data.length );
    }
    
    //------------------------------------------------------------------------------------------
    
    double sampleRateOut = 48000;
    
    AudioFormat b = new AudioFormat( (float)sampleRateOut, 16, 2, true, false );
    
    SourceDataLine.Info info = new DataLine.Info( SourceDataLine.class, b, 1024 );
    
    SourceDataLine line = (SourceDataLine)AudioSystem.getLine( info );
    line.addLineListener( new LineListener() {
      @Override
      public void update(LineEvent event) {
        System.out.println( System.currentTimeMillis() + ": --------------- " + event );
      }
    } );
    
    line.open();
    line.start();
    long started = System.currentTimeMillis();
    
    byte[] buf = new byte[line.getBufferSize()];
    
    int iCount = buf.length / 4;
    
    U: for (int u = 0; true; u++) {
      int bufferFilledOn = buf.length;
      I: for (int i = 0; i < iCount; i++) {
        
        int iSample = u * iCount + i;
        
        int iData = 4 * iSample;
        
        if (iData >= data.length) {
          bufferFilledOn = i * 4;
          break I;
        }
        
        @SuppressWarnings("unused")
        double t = ((double)iSample) / sampleRateOut;//time in seconds
        
        int left = Util.readWord( data, iData + 0, true );
        int right = Util.readWord( data, iData + 2, true );
        
        Util.writeWord( buf, 4 * i + 0, true, left );
        Util.writeWord( buf, 4 * i + 2, true, right );
      }
      
      if (bufferFilledOn == 0) break U;
      
      int offset = 0;
      while (offset < bufferFilledOn) {
        int toWrite = line.available();
        if (offset + toWrite > bufferFilledOn) {
          toWrite = bufferFilledOn - offset;
          if (toWrite <= 0) break;
        }
        offset += line.write( buf, offset, toWrite );
      }
    }
    
    line.drain();
    
    System.out.println( "TIME: " + (System.currentTimeMillis() - started) );
    System.out.println( "line.getLongFramePosition() = " + line.getLongFramePosition() );
    
    line.stop();
    line.close();
    System.out.println( "END" );
  }
}
